﻿#define _CRT_SECURE_NO_WARNINGS 1
//malloc的使用
//#include<stdio.h>
//#include<string.h>
//#include<errno.h>
//#include<stdlib.h>
//int main() {
//	//张三
//	//申请
//	int* p = (int*)malloc(20);
//	//对malloc的返回值进行判断
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//使用
//	int i = 0;
//	for (i = 0; i < 5; i++) {
//		p[i] = i + 1;//相当于*(p+i)=i+1;
//	}
//	for (i = 0; i < 5; i++) {
//		printf("%d ", p[i]);
//	}
//	//释放malloc开辟的空间并置空
//	free(p);
//	p = NULL;
//	return 0;
//
//}

//堆默认给malloc出来的空间初始化
//#include<stdio.h>
//#include<string.h>
//#include<errno.h>
//#include<stdlib.h>
//int main() {
//	//张三
//	//申请
//	int* p = (int*)malloc(20);
//	//对malloc的返回值进行判断
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//直接打印
//	int i = 0;
//	for (i = 0; i < 5; i++) {
//		printf("%d ", p[i]);
//	}
//	//释放malloc开辟的空间并置空
//	free(p);
//	p = NULL;
//	return 0;
//
//}

//举例使用calloc
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//#include<errno.h>
//int main() {
//	int* p = (int*)calloc(10, sizeof(int));
//	//判断是否开辟成功
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//使用空间
//	int i = 0;
//	for (i = 0; i < 10; i++) {
//		printf("%d ", p[i]);
//	}
//	//释放
//	free(p);
//	p = NULL;
//	return 0;
//}

////举例使用realloc：
//#include<stdio.h>
//#include<string.h>
//#include<errno.h>
//#include<stdlib.h>
//int main() {
//	int* p = (int*)malloc(20);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//使用
//	int i = 0;
//	for (i = 0; i < 5; i++) {
//		p[i] = i + 1;
//	}
//	int* ptr = (int*)realloc(p, 40);
//	if (ptr != NULL) {
//		p = ptr;
//	}
//	else {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//调整空间后使用
//	for (i = 0; i < 10; i++) {
//		p[i] = i + 1;
//	}
//	for (i = 0; i < 10; i++) {
//		printf("%d ", p[i]);
//	}
//	//释放malloc开辟的空间
//	free(p);
//	p = NULL;
//	return 0;
//}

//常见的动态内存错误
// 1.对NULL指针的解引用操作
// malloc / calloc动态开辟失败，没有检查返回值 
//#include<stdio.h>
//#include<stdlib.h>
//int main() {
//	int* p = (int*)malloc(20);
//	int i = 0;
//	for (i = 0; i < 5; i++) {
//		p[i] = i + 1;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}
/*
2.对动态开辟空间的越界访问
==动态开辟申请了多少字节就访问多少字节，不要越界==
*/
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//#include<errno.h>
//int main() {
//	int* p = (int*)malloc(20);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 10; i++) {  
//		p[i] = i + 1;
//	}
//	free(p);
//	p = NULL;
//	return 0;
//}
/*
3.对非动态开辟内存使用free释放
==在内存中开辟的空间如果不在堆上，不能用free释放==
*/
//#include<stdio.h>
//#include<stdlib.h>
//void test() {
//	int a = 10;
//	int* p = &a;
//	free(p);
//}
//int main() {
//	test();
//	return 0;
//}

/*
4.使用free释放动态开辟内存的一部分
==free释放空间必须提供要释放空间的起始位置==
*/
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//#include<errno.h>
//int main() {
//	int* p = (int*)malloc(40);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	int i = 0;
//	for (i = 0; i < 5; i++) {
//		*p = p + 1;
//		p++;
//	}
//	//释放
//	free(p);
//	p = NULL;
//	return 0;
//}

/*
5.对同一块动态内存多次释放
==（1）malloc / calloc动态开辟的空间是放过一次就不能再释放了。如果在第一次释放完之后置为空指针，第二次释放相当于释放空指针没有问题
（2）与别的函数交互时，不了解函数内部，很可能在函数内部已经释放过一次，又回到主函数中释放一次导致对同一块动态内存多次释放出错；又或者在函数的内部没有释放动态开辟的空间，回到主函数中也没有释放，导致内存泄漏，程序出错==
举例说明：
*/
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//#include<errno.h>
//int main() {
//	int* p = (int*)malloc(20);
//	if (p == NULL) {
//		printf("%s\n", strerror(errno));
//		return 1;
//	}
//	//使用
//	//...
//
//	//释放
//	free(p);
//	p = NULL;
//	free(p);//重复释放
//}

/*
6.动态开辟内存忘记释放（内存泄露）
==指的是动态开辟内存既不使用free释放，也不结束程序时造成的==
✨重点✨
==（1）malloc / calloc /realloc所申请的空间如果不想使用，需要用free释放。如果不释放，程序结束后，也会由操作系统回收。
（2）如果不使用free释放，程序也不结束，就会造成内存泄漏==
>意思是不用不还，程序也不结束，别人也无法利用这块空间，等于这块空间浪费掉了

==（3）回收内存的两种方式：
a. free掉
b. 程序结束后，由操作系统回收==
举例说明：
*/
//#include<stdio.h>
//#include<stdlib.h>
//#include<string.h>
//#include<errno.h>
//void test()
//{
//	int* p = (int*)malloc(100);
//	if (NULL != p)
//	{
//		*p = 20;
//	}
//}
//int main()
//{
//	test();
//	while (1);
//}


#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>

//笔试题1
//void GetMemory(char* p)
//{
//	p = (char*)malloc(100);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//}
//int main() {
//	Test();
//	return 0;
//}
/*
**结果运行出错的原因**：
（1）
调用GetMemory函数的时候，str的传参为值传递，p是str的临时拷贝，所以在GetMemory函数内部将动态开辟空间的地址存放在p中的时候，不会影响str，所以GetMemory函数返回之后，str中依然是NULL指针。strcpy函数就会调用失败，原因是对NULL指针的解引用操作，程序会崩溃
（2）
GetMemory函数内部malloc申请的空间没有机会释放，造成了内存泄漏
**本题目注意点**：
（1）注意这个程序想释放也释放不了，无论是在主函数中释放还是在test函数内部。因为一旦返回，就没有人记得动态开辟的空间在哪里，动态开辟空间的地址带不出来。
（2）而这道题的本意是想把100个字节的地址放到str里面，然后把hello world拷贝到str指向的空间里。
**改正该题目的错误**：
==正确修改1：（利用传值）==
*/
//char* GetMemory(char* p) {
//	p = (char*)malloc(100);
//	return p;
//}
///*
// 这里也是利用传值，但是GetMemory函数返回的是指针，p所指向的是malloc出来的空间，是在堆上，
// 出了函数不销毁这块空间，通过p返回的地址仍然可以找到这块空间
// 与原题传值对比：
//原题目中的p是函数的形参变量，形参变量是个临时变量，出了函数就销毁了。p销毁了，p这块空间还给操作系统，没人记得malloc申请的空间在哪里。
//
// */
//void Test(void) {
//	char* str = NULL;
//	str = GetMemory(str);
//	strcpy(str, "hello world");
//	printf(str);
//	free(str);  //使用完动态开辟的这块空间用free回收掉
//	str = NULL;
//	return 0;
//}
//int main() {
//	Test();
//	return 0;
//}

//==正确修改2：（利用传址）==
//void GetMemory(char** p) {
//	*p = (char*)malloc(100);//这样的话不通过返回值也能把地址带回来
//}
//void Test(void) {
//	char* str = NULL;
//	GetMemory(&str);//传址
//	strcpy(str, "hello world");
//	printf(str);//printf打印字符串的话只需要给一个地址
//	free(str);
//	str = NULL;
//	return 0;
//}
//int main() {
//	Test();
//	return 0;
//}
/*
本题涉及知识点
关于printf打印字符的两种方式：
*/
//int main() {
//	printf("hehe\n");//方法一：字符串作为一个表达式的时候，结果代表首字符的地址，这种打印形式相当于把字符串的首地址传给printf
//	printf("%s\n", "hehe");//方法二：用%s打印
//
//	return 0;
//}
/*
比如这句代码 char* p="hehe\n";
意思不是把字符串hehe\n放到p里面，而是把这个表达式字符串首地址h的地址放到p中

*/

//题目2：
//char* GetMemory(void)
//{
//	char p[] = "hello world";
//	return p;
//}
///*
//当进入Getmemory函数里面时，数组地址指向的空间是属于str的，一旦函数返回，数组地址指向的空间回收了，还给操作系统了。
//空间里面的内容就有可能被修改，而str记住地址又去打印这个空间的内容，有可能打印不出来，并且这种行为属于非法访问，因为这块空间被回收，已经不属于str了
//*/
//void Test(void)
//{
//	char* str = NULL;
//	str = GetMemory();
//	printf(str);
//}
//int main() {
//	Test();
//	return 0;
//}
/*
 教训：局部变量的地址不要随便返回
举例说明：
*/
//int* test() {
//	int a = 10;
//	return &a;
//}
//int main() {
//	int* p = test();
//	printf("%d\n", *p);
//	
//	return 0;
//}
/*
根据上面的讲解，这里打印的应该是随机值，而这里刚好打印出正确的结果，是怎么回事？难道这样写也正确吗？
答：这样写是错误的。之所以刚好打印出结果10，是因为恰好空间没有被改掉的情况。

*/

//int* test() {
//	int a = 10;
//	return &a;
//}
//int main() {
//	int* p = test();
//	printf("hehe\n");
//	printf("%d\n", *p);
//
//	return 0;
//}

//笔试题3：
//void GetMemory(char** p, int num)
//{
//	*p = (char*)malloc(num);
//}
//void Test(void)
//{
//	char* str = NULL;
//	GetMemory(&str, 100);
//	strcpy(str, "hello");
//	printf(str);
//	free(str);
//	str = NULL;
//}
//int main() {
//	Test();
//	return 0;
//}
/*
错误点：
malloc动态开辟的空间在使用完没有释放掉，造成内存泄漏
*/

//笔试题4：
void Test(void)
{
	char* str = (char*)malloc(100);
	strcpy(str, "hello");
	free(str);
	if (str != NULL)
	{
		strcpy(str, "world");
		printf(str);
	}
}
/*
错误点：
（1）free完动态开辟的空间应该及时将指针置空，这里没有置空
（2）str指向的空间已经回收了，因为str没有置为空指针，还能找到刚开辟的那块空间，但是这块空间已经回收给操作系统了，
     不属于str,不能用，拷贝时形成非法访问，此时的str是野指针
*/
//改正：
//void Test(void)
//{
//	char* str = (char*)malloc(100);
//	strcpy(str, "hello");
//	free(str);
//	str = NULL; //释放完之后置空指针
//	if (str != NULL)
//	{
//		strcpy(str, "world");
//		printf(str);
//	}
//}


/*
Nice校招笔试题
指出下面哪段程序有问题，并说明问题是什么。
（1）
*/
//int* fl(void) {
//	int x = 10;
//	return (&x);
//}
/*
该题是返回栈空间地址的问题。
如果有人接收了这个函数返回的指针，就会造成野指针的问题。
*/

//（2）
int* f2(void) {
	int* ptr;
	*ptr = 10;
	return ptr;
}
/*
指针ptr没有初始化，没有任何指向，里面是随机值。
不知道ptr指向谁，直接对它解引用
对野指针解引用，还把10放进去，非法访问内存。
*/